//
//  ViewController.m
//  NSthread
//
//  Created by admin on 2019/8/14.
//  Copyright © 2019 猫爷爷的酱醋茶. All rights reserved.
//

/*
 
 iOS中多线程的技术方案
 
 
    NSThread - Foundation框架提供的一个类- OC语言
     a）本质是对pthread的封装，线程生命周期也需要程序员来管理
     b）使用更加面向对象，简单易用，可以直接操作线程对象
     c）偶尔使用
 
    创建一个多线程对象，会将线程对象放入到内存中的可调度线程池（一个数组集合）中，当执行start方法的时候，并没有立即执行，
    线程什么时候执行，是不确定的，而是准备就绪Runnable的状态，等待CPU执行，CPU是切换执行的。
 
线程的状态有
    - 新建状态（初始化）、就绪状态（start方法）、阻塞状态（sleep方法）、退出状态（exit方法）
    线程结束之后，会自动销毁。
 
 线程的属性
    - 线程的名称 thread.name
        可以给线程的属性name命名，这样在程序出错的时候可以清除的知道是哪里的错误
    - 线程的优先级
    设置线程的优先级（0-1）默认为0.5 数字越大优先级越高 执行的概率就大
    thread.threadPriority = 1.0;
 
 线程是不安全的。
    使用互斥锁能有效防止因多线程抢夺资源造成的数据安全问题。目的是为了让线程同步：多条线程按顺序地执行任务
    互斥锁，就是使用了线程的同步技术。
 
    @synchronized(锁对象)
     {
     // 需要锁定的代码
     }
 
    每一个对象内部都有一个锁（变量），当有线程要进入synchronized到代码块中会先检查对象的锁是打开还是关闭，默认状态下是打开状态（1），
    如果有线程进入到代码块中，会先上锁（0），如果上锁了，再有线程要执行就像等待，直到锁打开才能进入。
 
 互斥锁和自旋锁
    互斥锁，如果发现其他进程正在执行锁定代码，线程会进入休眠（就绪状态），等其他线程执行完毕到打开锁后，线程会被唤醒。
    自旋锁，如果发现有其他线程正在锁定代码，线程会用死循环的方式，一直等待锁定代码执行完成，自旋锁更适合执行不耗时的代码。
        自旋 -> 一值循环
 */

#import "ViewController.h"

@interface ViewController ()

//模拟火车票
@property(nonatomic,assign)int trainTicket;

//原子属性的变量 - 无法保存同时读写的正确性。所以推荐使用nonatomic
@property(atomic,copy)NSString *name;


@end

@implementation ViewController

//同时重写setter和getter方法，不会自动生成_name 成员变量

//需要为属性生成对象的成员变量
@synthesize name = _name;

- (NSString *)name
{
    return _name;
}
//模拟原子属性
- (void)setName:(NSString *)name
{
    @synchronized (self)
    {
        _name = name;
    }
}



- (void)viewDidLoad
{
    [super viewDidLoad];
   
    self.trainTicket = 10;
    
    NSThread *threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(dataSecurity) object:nil];
    [threadOne start];
    
    NSThread *threadTwo = [[NSThread alloc]initWithTarget:self selector:@selector(dataSecurity) object:nil];
    [threadTwo start];
    
}

//多个线程调用一个对象，可能会出现问题
-(void)dataSecurity
{
    while (1)
    {
        //模拟耗时操作
        [NSThread sleepForTimeInterval:1.0];
        
        //为这段代码加一把安全锁，也叫做互斥锁，为了让线程同步 - 让此代码同一时间内只能让一个线程访问
        //任意一个对象内部都有一把锁，默认打开，只要有线程访问就关闭，直到当前线程执行完毕
        @synchronized (self) {
            
            if (self.trainTicket > 0)
            {
                self.trainTicket -= 1;
                NSLog(@"剩余%d张票",self.trainTicket);
            }else{
                NSLog(@"车票已售空");
                break;
            }
        }
       
    }
    
}


//创建多线程的方式
//方式三 - 在后台执行
-(void)methodThree
{
    [self  performSelectorInBackground:@selector(demoOne) withObject:nil];
    [self performSelectorInBackground:@selector(demoTwo:) withObject:@"要成为IOS高级开发者"];
}

//方式二
-(void)methodTwo
{
    
    //相对于方式一是一种结合的写法，直接启用
    //线程什么时候执行，是不确定的
    [NSThread detachNewThreadSelector:@selector(demoOne) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(demoTwo:) toTarget:self withObject:@"IOS中级工程师"];
    
}
//方式一
-(void)methodOne
{
    NSThread *threadOne = [[NSThread alloc]initWithTarget:self selector:@selector(demoOne) object:nil];
    [threadOne start];
    //可以给线程的属性name命名，这样在程序出错的时候可以清除的知道是哪里的错误
    threadOne.name = @"threadOne";
    
    NSThread *threadTwo = [[NSThread alloc]initWithTarget:self selector:@selector(demoTwo:) object:@"IOS开发者"];
    [threadTwo start];
    
}

-(void)demoTwo:(NSString *)str
{
    NSLog(@"猫爷爷的酱醋茶，%@,继续努力呀!%@",str,[NSThread currentThread]);
}


-(void)demoOne
{
    NSLog(@"猫爷爷的酱醋茶，继续努力呀!%@",[NSThread currentThread]);
}

@end
